package com.winit.core.task;

import com.winit.common.util.ConfigUtil;
import com.winit.common.util.Constants;
import com.winit.common.util.MailUtil;
import com.winit.common.util.SpringContextHolder;
import com.winit.core.server.Server;
import com.winit.deploy.bean.DeployStepEnum;
import com.winit.deploy.bean.WinitAndroidInfo;
import com.winit.deploy.bean.WinitTomcatInfo;
import com.winit.deploy.server.WinitAndroidServer;
import com.winit.deploy.server.WinitTomcatServer;
import com.winit.deploy.service.DeployAuthorizeService;
import com.winit.deploy.task.tomcatTask.DeployServerCheckTask;
import com.winit.deploy.task.tomcatTask.DeployStartServerTask;
import com.winit.deploy.util.DeployHandel;
import com.winit.project.util.ProjectUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.StringUtils;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.*;

/**
 * Created by wangjian on 16/8/9.
 */
public class ChainBaseTask extends Task {

    private static final Logger logger = LoggerFactory.getLogger(ChainBaseTask.class);

    /**
     * 任务列表
     * 一键发布时为6个任务
     * 单独执行发布步骤时,taskNodeList长度为1,另需要传入该步骤序号,从0开始
     * 0 准备 1 停 2 备份 3 安装 4 启动 5 验证
     */
    private List<TaskNode> taskNodeList;
    //单独执行发布步骤时,需要传入当前步骤的序号
//    private Integer step = -1;
    //任务列表中,每个任务执行的返回状态列表
    private String[] status = new String[7];
    private List<String> taskNodeStatus = new ArrayList<>();

    public List<TaskHandler> handlers = new ArrayList<>();
    //是否暂停中
    private boolean isPause = Boolean.FALSE;
    //远程终端对象
    private Server server = null;
    //是否需要检查tomcat启动状态
    private boolean isCheckServer = false;

    public ChainBaseTask(String taskId, Integer expireTime, List<TaskNode> taskNodeList) {
        super(taskId, expireTime);
        this.taskNodeList = taskNodeList;
    }

    public ChainBaseTask(String taskId, Integer expireTime, List<TaskNode> taskNodeList, TaskHandler taskHandler) {
        super(taskId, expireTime);
        this.taskNodeList = taskNodeList;
        this.handlers.add(taskHandler);
    }

    public void setServer(Server server) {
        this.server = server;
    }

    public void addListener(TaskHandler taskHandler) {
        this.handlers.add(taskHandler);
    }

    @Override
    public TaskStatus call() throws Exception {
        logger.info("ChainBaseTask:执行线程开始.taskId=" + this.taskId);

        if (!this.canTaskRun()) {
            logger.error("ChainBaseTask:执行线程,任务不可执行.taskId=" + this.taskId);
            this.taskStatus = TaskStatus.STATUS_ERROR;
            this.message = "ERROR:任务不可执行.";
            this.publishChainTaskStatusAndMessage();
            this.taskClose();
            return this.taskStatus;
        }

        this.taskStatus = TaskStatus.STATUS_RUNNING;
        this.message = "任务执行中";
        this.publishChainTaskStatusAndMessage();

        try {
            this.validate();
        } catch (Exception e) {
            logger.error("ChainBaseTask:执行线程,参数校验失败.taskId=" + this.taskId);
            this.taskStatus = TaskStatus.STATUS_PARAM_ERROR;
            this.message = "ERROR:参数校验失败." + e.getMessage();
            this.publishChainTaskStatusAndMessage();
            this.taskClose();
            return this.taskStatus;
        }
        ExecutorService executor = Executors.newCachedThreadPool();
        try {
            try {
                this.server.setConnectionTimeout(Constants.SSH_CONNECT_TIMEOUT);
                this.server.connect();
            } catch (Exception e) {
                logger.error("ChainBaseTask:连接终端失败", e);
                throw new Exception("连接终端失败,请重试或联系beetle管理员!");
            }
            this.run(executor);
            if (!this.isTaskStop) {
                this.taskStatus = TaskStatus.STATUS_SUCCESS;
                this.message = "任务执行完成";
            }
        } catch (Exception e) {
            logger.error("ChainBaseTask:执行任务异常,e:" + e + ",taskId=" + this.taskId);
            this.taskStatus = TaskStatus.STATUS_EXCEPTION;
            this.message = "ERROR:任务执行异常." + e.getMessage();
            throw e;
        } finally {
            executor.shutdownNow();
            this.server.disConnect();
            this.publishChainTaskStatusAndMessage();
            this.taskClose();
            this.createCheckTask();
            return this.taskStatus;
        }
    }

    private boolean validate() throws Exception {
        logger.info("ChainBaseTask:执行线程,校验参数,taskId=" + this.taskId);
        if (this.server == null) {
            logger.error("ChainBaseTask:执行线程,server为null");
            throw new Exception("执行线程,server为null.请联系beetle管理员!");
        }
        Integer size = this.taskNodeList.size();
        if (size == 0) {
            logger.error("ChainBaseTask:执行线程,线程列表为空.size=" + size + ",taskId=" + this.taskId);
            throw new Exception("执行线程,参数检验失败,线程列表为空.");
        }

        return true;
    }

    private void run(ExecutorService executor) throws Exception {
        for (int i = 0; i < this.taskNodeList.size(); i++) {
            TaskNode taskNode = this.taskNodeList.get(i);
            Integer index = taskNode.index == -1 ? i : taskNode.index;
            while (this.isPause) {
                Thread.sleep(2000);
                if (!this.isPause || this.isTaskStop) {
                    if (!this.isPause) {
                        this.taskStatus = TaskStatus.STATUS_RUNNING;
                        this.message = "任务执行中";
                        this.publishChainTaskStatusAndMessage();
                        taskNode.task.isContinue = true;
                    }
                    break;
                }
            }
            if (!this.canTaskRun()) {
                logger.error("ChainBaseTask:执行线程,任务不可执行.taskId=" + this.taskId);
                this.taskStatus = TaskStatus.STATUS_ERROR;
                this.message = "ERROR:任务不可执行.";
                this.publishChainTaskStatusAndMessage();
                this.setAndPublishTaskNodeStatus(TaskStatus.STATUS_STOP, index);
                break;
            }
            if (this.isTaskStop) {
                logger.info("ChainBaseTask:任务被停止,退出.taskId=" + this.taskId);
                this.taskStatus = TaskStatus.STATUS_STOP;
                this.message = "任务被停止";
                this.setAndPublishTaskNodeStatus(TaskStatus.STATUS_STOP, index);
                break;
            }

            this.setAndPublishTaskNodeStatus(TaskStatus.STATUS_RUNNING, index);
            Task task = taskNode.task;
            if (task == null) {
                logger.error("ChainBaseTask:taskNode内任务为null");
                throw new Exception("taskNode内任务为null");
            }

            if (task instanceof DeployStartServerTask) {
                this.isCheckServer = true;
            }

            Future<TaskStatus> future = executor.submit(task);

            try {
                TaskStatus s = future.get(taskNode.task.expireTime, TimeUnit.SECONDS);
                /**
                 * 执行单步发布时,需要将状态放到list内指定的位置
                 * 一键发布时,按顺序保存
                 */

                if (s != TaskStatus.STATUS_SUCCESS) {
                    if (taskNode.needFailConfirm) {
                        this.pauseT();
                        this.taskStatus = TaskStatus.STATUS_PAUSE;
                        this.message = "任务暂停";
                        s = TaskStatus.STATUS_PAUSE;
                        this.publishChainTaskStatusAndMessage();
                        i--;
                    } else {
                        this.taskStatus = s;
                        this.message = "ERROR:任务执行失败";
                        i--;
                        this.stopTask();
                    }
                }
                this.setAndPublishTaskNodeStatus(s, index);
            } catch (TimeoutException e) {
                logger.error("ChainBaseTask:执行任务超时.taskId=" + taskNode.task.taskId + ",expireTime=" + taskNode.task.expireTime + ",e:" + e);
                this.taskStatus = TaskStatus.STATUS_TIMEOUT;
                this.message = "ERROR:任务执行超时,请联系系统管理员!";
                this.setAndPublishTaskNodeStatus(this.taskStatus, index);
                i--;
                this.stopTask();
            } catch (Exception e) {
                logger.error("ChainBaseTask:执行任务异常.taskId=" + taskNode.task.taskId + ",e:" + e);
                if (taskNode.needFailConfirm) {
                    this.pauseT();
                    this.taskStatus = TaskStatus.STATUS_PAUSE;
                    this.message = "任务执行异常暂停," + e.getMessage();
                    this.setAndPublishTaskNodeStatus(TaskStatus.STATUS_PAUSE, index);
                    this.publishChainTaskStatusAndMessage();
                    i--;
                } else {
                    this.taskStatus = TaskStatus.STATUS_EXCEPTION;
                    this.message = "ERROR:任务执行异常." + e.getMessage();
                    this.setAndPublishTaskNodeStatus(this.taskStatus, index);
                    this.publishChainTaskStatusAndMessage();
                    i--;
                    this.stopTask();
                }
            }
        }
    }

    private void setAndPublishTaskNodeStatus(TaskStatus taskStatus, Integer index) {
//        if(this.taskNodeList.size() == 1){
//            this.status[this.step] = taskStatus.getValue();
//        }else{
        this.status[index] = taskStatus.getValue();
//        }

        if (this.handlers.size() == 0) {
            return;
        }
        this.taskNodeStatus.clear();
        Collections.addAll(this.taskNodeStatus, this.status);
        for (TaskHandler handler : this.handlers) {
            handler.handleTaskNodeStatus(this.taskId, this.taskNodeStatus);
        }
    }

    private void publishChainTaskStatusAndMessage() {
        if (this.handlers.size() == 0) {
            return;
        }
        for (TaskHandler handler : this.handlers) {
            handler.handleChainTaskStatus(this.taskId, this.taskStatus.getValue());
            handler.handleChainTaskMessage(this.taskId, this.message);
        }
    }

    private void publishChainTaskMessage() {
        if (this.handlers.size() == 0) {
            return;
        }
        for (TaskHandler handler : this.handlers) {
            handler.handleChainTaskMessage(this.taskId, this.message);
        }
    }

    /**
     * 任务完成后调用 删除缓存对象
     */
    public void taskClose() {
        try {
            this.sendMailForWho();
        } catch (Exception e) {
            logger.error("com.winit.core.task.ChainBaseTask.sendMailForWho 异常", e);
        }
        logger.info("Task:任务执行完成,删除缓存对象.taskId=" + this.taskId);
        this.taskContainer.removeTask(this.taskId);
    }

    public interface TaskHandler {

        void handleTaskNodeStatus(String taskId, List<String> taskNodeStatus);

        void handleChainTaskStatus(String taskId, String taskStatus);

        void handleChainTaskMessage(String taskId, String message);
    }

    /**
     * 暂停
     */
    private void pauseT() {
        logger.info("ChainBaseTask:当前线程休眠,taskId=" + this.taskId);
        this.isPause = Boolean.TRUE;
    }

    /**
     * 继续执行当前线程
     */
    public void continueTask() {
        logger.info("ChainBaseTask:当前线程结束休眠,继续执行,taskId=" + this.taskId);
        this.isPause = Boolean.FALSE;
        this.taskStatus = TaskStatus.STATUS_CONTINUE;
        this.message = "任务执行中";
        this.publishChainTaskStatusAndMessage();
    }

    //线程开始时，判断是否可以运行 避免发版误操作
    private boolean canTaskRun() {
        DeployAuthorizeService authorizeService = SpringContextHolder.getBean(DeployAuthorizeService.class);
        //是否发布过程中点击终止
        String env = this.taskId.split("-")[0];
        if (authorizeService.getStopDeploytip(env)) {
            logger.info("com.winit.core.task.ChainBaseTask.canTaskRun 发布被终止 taskId=" + this.taskId);
            this.isTaskStop = true;
            return false;
        }

        //是否发布线上
        if (!this.taskId.startsWith("online-")) {
            return true;
        }
        //发布who不需要授权
        if (this.taskId.contains("-who-")) {
            return true;
        }

        //是否已授权
        if (!authorizeService.getAuthorize().getBoolean("authorize")) {
            logger.info("com.winit.core.task.ChainBaseTask.canTaskRun 线上未授权 taskId=" + this.taskId);
            this.isTaskStop = true;
            return false;
        }
        return true;
    }

    //启动tomcat完成后，创建检查task
    private void createCheckTask() {
        if (!this.isCheckServer) {
            return;
        }
        WinitTomcatServer tomcatServer = (WinitTomcatServer) this.server;
        WinitTomcatInfo tomcatInfo = tomcatServer.getWinitTomcatInfo();
        Task task = new DeployServerCheckTask(taskId, 300, tomcatServer);
        TaskNode taskNode = new TaskNode(task, false, DeployStepEnum.CHECK.getIndex());
        List<TaskNode> taskNodeList = new ArrayList<>();
        taskNodeList.add(taskNode);
        ChainBaseTask.TaskHandler handler = DeployHandel.getDeployHandler(tomcatInfo.getEnvName(), tomcatInfo.getAreaName(), tomcatInfo.getQueueName(), tomcatInfo.getProjectName(), tomcatInfo.getEditionNum());
        ChainBaseTask chainBaseTask = new ChainBaseTask(taskId, 3600, taskNodeList, handler);
        chainBaseTask.setServer(this.server);
        ServerCheckTaskContainer taskContainer = ServerCheckTaskContainer.getInstance();
        taskContainer.addTask(chainBaseTask);
    }

    //发布who时，结束后发送邮件通知
    private void sendMailForWho() {
        WinitAndroidServer androidServer = (WinitAndroidServer) this.server;
        WinitAndroidInfo androidInfo = androidServer.getAndroidInfo();
        logger.info("com.winit.core.task.ChainBaseTask.sendMailForWho start ");
        if (!this.taskId.contains("-who-")) {
            return;
        }
        String autoDeployWho = ConfigUtil.getValue("auto_deploy_who");
        if (StringUtils.isEmpty(autoDeployWho) || !"true".equals(autoDeployWho)) {
            return;
        }
        String envs = ConfigUtil.getValue("auto_deploy_who_env");
        if (StringUtils.isEmpty(envs) || !Arrays.asList(envs.split(",")).contains(androidInfo.getEnvName())) {
            return;
        }

        String subject = "【重要】who自动发布通知";
        String body = "<!DOCTYPE html><html lang=\"en\"><head><meta charset=\"UTF-8\"><title>Title</title></head><body>" +
                "任务：" + this.taskId + "<br>环境：" + androidInfo.getEnvName() + "<br>区域：" + androidInfo.getAreaName() +
                "<br>应用：" + androidInfo.getProjectName() + "<br>版本：" + androidInfo.getEditionNum() +
                "<br>状态：" + this.taskStatus + "<br>描述：" + this.message + "</body></html>";
        MailUtil.sendMail(ConfigUtil.getValue("auto_deploy_who_notify_user"), subject, body);
    }

}
